#ifndef __color_H__
#define __color_H__

#include "rendVect.hpp"
#include "rendPixel.hpp"


namespace rend
{

///////////////////////////////////////////////////////////////////////
// color-space-based vector derivations
///////////////////////////////////////////////////////////////////////

typedef Pixel4< uint32, 8, 8, 8, 8 >	PixelRGBA;
typedef Pixel4< uint32, 8, 8, 8, 8 >	PixelARGB;
typedef Pixel4< uint32, 8, 8, 8, 8 >	PixelBGRA;
typedef Pixel4< uint32, 8, 8, 8, 8 >	PixelABGR;


class Color : public vectr< 3, Color >
{
public:

	Color& setR(const float c)
		{ return set(0, c); }

	Color& setG(const float c)
		{ return set(1, c); }

	Color& setB(const float c)
		{ return set(2, c); }

	float getR() const
		{ return get(0); }

	float getG() const
		{ return get(1); }

	float getB() const
		{ return get(2); }

	Color()
	{
		setR(0.f);
		setG(0.f);
		setB(0.f);
	}

	Color(float r,
		  float g,
		  float b)
	{
		setR(r);
		setG(g);
		setB(b);
	}

	static const Color Black;
	static const Color White;
	static const Color Red;
	static const Color Green;
	static const Color Blue;

	PixelRGBA getAsRGBA() const;

	PixelARGB getAsARGB() const;

	PixelBGRA getAsBGRA() const;

	PixelABGR getAsABGR() const;

	Color& setAsRGBA(const PixelRGBA& val);

	Color& setAsARGB(const PixelARGB& val);

	Color& setAsBGRA(const PixelBGRA& val);

	Color& setAsABGR(const PixelABGR& val);

	Color& setAsHSB(float hue, float saturation, float brightness);

	Color& saturate()
	{
		if (getR() < 0.f)
			setR(0.f);
		else
		if (getR() > 1.f)
			setR(1.f);

		if (getG() < 0.f)
			setG(0.f);
		else
		if (getG() > 1.f)
			setG(1.f);

		if (getB() < 0.f)
			setB(0.f);
		else
		if (getB() > 1.f)
			setB(1.f);

		return *this;
	}

	operator vect3& ()
	{ return reinterpret_cast< vect3& >(*this); }
};


inline PixelRGBA Color::getAsRGBA() const
{
	return PixelRGBA(getR(),
					 getG(),
					 getB(),
					 1.f);
}


inline PixelARGB Color::getAsARGB() const
{
	return PixelARGB(1.f,
					 getR(),
					 getG(),
					 getB());
}


inline PixelBGRA Color::getAsBGRA() const
{
	return PixelBGRA(getB(),
					 getG(),
					 getR(),
					 1.f);
}


inline PixelABGR Color::getAsABGR() const
{
	return PixelABGR(1.f,
					 getB(),
					 getG(),
					 getR());
}


inline Color& Color::setAsRGBA(const PixelRGBA& pix)
{
	setR(pix.get_f3());
	setG(pix.get_f2());
	setB(pix.get_f1());

	return *this;
}


inline Color& Color::setAsARGB(const PixelARGB& pix)
{
	setR(pix.get_f2());
	setG(pix.get_f1());
	setB(pix.get_f0());

	return *this;
}


inline Color& Color::setAsBGRA(const PixelBGRA& pix)
{
	setR(pix.get_f1());
	setG(pix.get_f2());
	setB(pix.get_f3());

	return *this;
}


inline Color& Color::setAsABGR(const PixelABGR& pix)
{
	setR(pix.get_f0());
	setG(pix.get_f1());
	setB(pix.get_f2());

	return *this;
}


class ColorA : public vectr< 4, ColorA >
{
public:

	ColorA& setR(const float c)
		{ return set(0, c); }

	ColorA& setG(const float c)
		{ return set(1, c); }

	ColorA& setB(const float c)
		{ return set(2, c); }

	ColorA& setA(const float c)
		{ return set(3, c); }

	float getR() const
		{ return get(0); }

	float getG() const
		{ return get(1); }

	float getB() const
		{ return get(2); }

	float getA() const
		{ return get(3); }

	ColorA()
	{
		setR(0.f);
		setG(0.f);
		setB(0.f);
		setA(1.f);
	}

	ColorA(float r,
		   float g,
		   float b,
		   float a = 1.f)
	{
		setR(r);
		setG(g);
		setB(b);
		setA(a);
	}

	static const ColorA Black;
	static const ColorA White;
	static const ColorA Red;
	static const ColorA Green;
	static const ColorA Blue;

	PixelRGBA getAsRGBA() const;

	PixelARGB getAsARGB() const;

	PixelBGRA getAsBGRA() const;

	PixelABGR getAsABGR() const;

	ColorA& setAsRGBA(const PixelRGBA& val);

	ColorA& setAsARGB(const PixelARGB& val);

	ColorA& setAsBGRA(const PixelBGRA& val);

	ColorA& setAsABGR(const PixelABGR& val);

	ColorA& setAsHSB(float hue, float saturation, float brightness);

	ColorA& saturate()
	{
		if (getR() < 0.f)
			setR(0.f);
		else
		if (getR() > 1.f)
			setR(1.f);

		if (getG() < 0.f)
			setG(0.f);
		else
		if (getG() > 1.f)
			setG(1.f);

		if (getB() < 0.f)
			setB(0.f);
		else
		if (getB() > 1.f)
			setB(1.f);

		if (getA() < 0.f)
			setA(0.f);
		else
		if (getA() > 1.f)
			setA(1.f);

		return *this;
	}

	operator vect4& ()
	{ return reinterpret_cast< vect4& >(*this); }
};


inline PixelRGBA ColorA::getAsRGBA() const
{
	return PixelRGBA(getR(),
					 getG(),
					 getB(),
					 getA());
}


inline PixelARGB ColorA::getAsARGB() const
{
	return PixelARGB(getA(),
					 getR(),
					 getG(),
					 getB());
}


inline PixelBGRA ColorA::getAsBGRA() const
{
	return PixelBGRA(getB(),
					 getG(),
					 getR(),
					 getA());
}


inline PixelABGR ColorA::getAsABGR() const
{
	return PixelABGR(getA(),
					 getB(),
					 getG(),
					 getR());
}


inline ColorA& ColorA::setAsRGBA(const PixelRGBA& pix)
{
	setR(pix.get_f3());
	setG(pix.get_f2());
	setB(pix.get_f1());
	setA(pix.get_f0());

	return *this;
}


inline ColorA& ColorA::setAsARGB(const PixelARGB& pix)
{
	setR(pix.get_f2());
	setG(pix.get_f1());
	setB(pix.get_f0());
	setA(pix.get_f3());

	return *this;
}


inline ColorA& ColorA::setAsBGRA(const PixelBGRA& pix)
{
	setR(pix.get_f1());
	setG(pix.get_f2());
	setB(pix.get_f3());
	setA(pix.get_f0());

	return *this;
}


inline ColorA& ColorA::setAsABGR(const PixelABGR& pix)
{
	setR(pix.get_f0());
	setG(pix.get_f1());
	setB(pix.get_f2());
	setA(pix.get_f3());

	return *this;
}

} // namespace rend

#endif // __color_H__
